
#include "BSP_Buzzer.h"
#include "BSP_Led.h"
#include "Timer.h"
#include "Indecator.h"

#define INDE_MAX		4

static Indecator_T* indecatorTable[INDE_MAX];

static uint32_t indecatorTick;
static Timer_T indecatorTimer;

static inline void Object_Act(IndecatorObject_T* obj, uint32_t state){
	if(obj->action){
		obj->action(obj->objId, state);
	}
}

static inline IndecatorState_T* Setting_ApplyState(IndecatorSetting_T* setting, uint8_t index){
	IndecatorState_T* state = &setting->states[index];
	
	if(state->time == 0){
		//持续时间为0的状态为无效状态，切换到下一个状态
		index++;
		index &= 0x01;
		state = &setting->states[index];
	}
	setting->current = index;
	return state;
}

static inline IndecatorSetting_T* Indecator_CurrentSetting(Indecator_T* indecator){
	return &indecator->settings[indecator->current];
}

static void Indecator_ApplySetting(Indecator_T* indecator, uint8_t settingID, uint8_t stateId, uint32_t tick){
	IndecatorSetting_T* curSetting = &indecator->settings[settingID];
	IndecatorState_T* curState = Setting_ApplyState(curSetting, stateId);
	Object_Act(&indecator->obj, curState->state);
	indecator->current = settingID;
	indecator->stateTick = tick + curState->time;
	indecator->settingTick = tick + curSetting->duration;
}


static void Indecator_SwitchSetting(Indecator_T* indecator, uint32_t tick){
	uint8_t index = indecator->current != Setting_Permanent ? Setting_Permanent : Setting_Temp;

	Indecator_ApplySetting(indecator, index, State_Off, tick);
}

static void Indecator_SwitchState(Indecator_T* indecator, uint32_t tick){
	IndecatorSetting_T* setting = Indecator_CurrentSetting(indecator);
	uint8_t index = setting->current != State_On ? State_On : State_Off;
	IndecatorState_T* state = Setting_ApplyState(setting, index);
	Object_Act(&indecator->obj, state->state);
	indecator->stateTick = tick + state->time;
}



static void Indecator_UpdateState(Indecator_T* indecator, uint32_t tick){
	IndecatorSetting_T* curSetting = Indecator_CurrentSetting(indecator);
	
	/*
	duration == 0时，setting 不切换
	duration > 0时，setting过期后切换
	*/
	if(curSetting->duration > 0){
		if(indecator->settingTick == tick){
			Indecator_SwitchSetting(indecator, tick);
		}
	}
	
	if(indecator->stateTick == tick){
		//切换当前setting的状态
		Indecator_SwitchState(indecator, tick);
	}
}

static void IndecatorCallback(void){
	int i;
	++indecatorTick;
	for(i = 0; i < sizeof(indecatorTable)/sizeof(indecatorTable[0]); i++){
		if(indecatorTable[i]){
			Indecator_UpdateState(indecatorTable[i], indecatorTick);
		}
	}
}

void Indecator_Init(void){
	if(Timer_IsActive(&indecatorTimer)){
		return ;
	}
	indecatorTick = 0;	
	Timer_Setup(&indecatorTimer, 1, 1, 0, IndecatorCallback);
	Timer_Start(&indecatorTimer);
}

void Indecator_Attach(Indecator_T* indecator, const IndecatorObject_T* obj){
	if(indecator && obj ){
		indecator->obj = *obj;
	}
}

void Indecator_Unregister(Indecator_T* indecator){
	int i;

	for(i = 0; i < INDE_MAX; i++){
		if(indecatorTable[i] == indecator){
			indecatorTable[i] = 0;
			break;
		}
	}
}

int Indecator_Register(Indecator_T* indecator){
	int i;
	
	Indecator_Unregister(indecator);
	
	for(i = 0; i < INDE_MAX; i++){
		if(!indecatorTable[i]){
			indecatorTable[i] = indecator;
			return i;
		}
	}
	
	return -1;
}




static void Indecator_UpdateSetting(Indecator_T* indecator, uint8_t index, const IndecatorSetting_T* setting){
	indecator->settings[index] = *setting;
}

#if 0
void Indecator_Set(uint8_t id, uint8_t activeSetting, const IndecatorSetting_T* permanent, const IndecatorSetting_T* temp){
	Indecator_T* indecator = &indecatorTable[id];
	
	if(permanent){
		Indecator_UpdateSetting(indecator, Setting_Permanent, permanent);
	}
	
	if(temp){
		Indecator_UpdateSetting(indecator, Setting_Temp, temp);
	}
	
	if(activeSetting < Setting_MAX){
		Indecator_ApplySetting(indecator, activeSetting, State_Off, indecatorTick);
	}
}
#endif 

void Indecator_Set(Indecator_T* indecator, uint8_t activeSetting, const IndecatorSetting_T* permanent, const IndecatorSetting_T* temp){
	
	if(permanent){
		Indecator_UpdateSetting(indecator, Setting_Permanent, permanent);
	}
	
	if(temp){
		Indecator_UpdateSetting(indecator, Setting_Temp, temp);
	}
	
	if(activeSetting < Setting_MAX){
		IndecatorSetting_T* setting = &indecator->settings[activeSetting];
		
		Indecator_ApplySetting(indecator, activeSetting, setting->current, indecatorTick);
	}
}



